#include "map_mesh.h"
#include "filesystem/be_filesystem.h"

MeshMap::MeshMap(boost::shared_ptr<btDynamicsWorld> ownerWorld)
  : Map( MESHMAP, ownerWorld )
{
}


void MeshMap::load(const char* mapname)
{
	string mapfile = "maps/";
	mapfile.append(mapname);
	mapfile.append("/map");

	BeFile befileMap;

	// open and parse file
	if ( BeFilesystem::Instance()->load(befileMap, mapfile) )
	{
		string maptype;
		string version;
		string obj;
		float scaleX = 1; float scaleY = 1; float scaleZ = 1;
		float posX = 0; float posY = 0; float posZ = 0;
		float rotX = 0; float rotY = 0; float rotZ = 0;

		float player_posX = 0; float player_posY = 0; float player_posZ = 0;
		float player_rotX = 0; float player_rotY = 0; float player_rotZ = 0;

		std::string line;
		while ( befileMap.getLine(line) )
		{
			// trim spaces
			while ( parseH.beginMatchesStrip( " ", line ) ) {};
			while ( parseH.beginMatchesStrip( "\t", line ) ) {};
			// remove comment lines
			if ( parseH.beginMatchesStrip( "//", line ) ) line.clear();
			if ( parseH.beginMatchesStrip( "#", line ) ) line.clear();

			if ( line.length() > 1 )
			{
				line.append(" ");

				string param = parseH.returnUntillStrip( "=", line );

				// trim spaces at back
				while ( parseH.endMatchesStrip( " ", param ) ) {};
				while ( parseH.endMatchesStrip( "\t", param ) ) {};

				// trim spaces from begin of line again
				while ( parseH.beginMatchesStrip( " ", line ) ) {};
				while ( parseH.beginMatchesStrip( "\t", line ) ) {};

				if ( param == "stuntcoureur_maptype" )
					maptype = parseH.returnUntillStrip( " ", line );

				else if ( param == "stuntcoureur_version" )
					version = parseH.returnUntillStrip( " ", line );

				else if ( param == "obj" )
					obj = parseH.returnUntillStrip( " ", line );

				else if ( param == "scale" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					string y = parseH.returnUntillStrip( " ", line );
					string z = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) scaleX = atof( x.c_str() );
					if ( !y.empty() ) scaleY = atof( y.c_str() );
					if ( !z.empty() ) scaleZ = atof( z.c_str() );
				}

				else if ( param == "position" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					string y = parseH.returnUntillStrip( " ", line );
					string z = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) posX = atof( x.c_str() );
					if ( !y.empty() ) posY = atof( y.c_str() );
					if ( !z.empty() ) posZ = atof( z.c_str() );
				}

				else if ( param == "rotation" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					string y = parseH.returnUntillStrip( " ", line );
					string z = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) rotX = atof( x.c_str() );
					if ( !y.empty() ) rotY = atof( y.c_str() );
					if ( !z.empty() ) rotZ = atof( z.c_str() );
				}


				else if ( param == "player_startposition" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					string y = parseH.returnUntillStrip( " ", line );
					string z = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) player_posX = atof( x.c_str() );
					if ( !y.empty() ) player_posY = atof( y.c_str() );
					if ( !z.empty() ) player_posZ = atof( z.c_str() );
				}

				else if ( param == "player_rotation" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					string y = parseH.returnUntillStrip( " ", line );
					string z = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) player_rotX = atof( x.c_str() );
					if ( !y.empty() ) player_rotY = atof( y.c_str() );
					if ( !z.empty() ) player_rotZ = atof( z.c_str() );
				}

				else if ( param == "finishline_position_rotation" )
				{
					float finish_posX = 0; float finish_posY = 0; float finish_posZ = 0;
					float finish_rotX = 0; float finish_rotY = 0; float finish_rotZ = 0;

					string x = parseH.returnUntillStrip( " ", line );
					string y = parseH.returnUntillStrip( " ", line );
					string z = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) finish_posX = atof( x.c_str() );
					if ( !y.empty() ) finish_posY = atof( y.c_str() );
					if ( !z.empty() ) finish_posZ = atof( z.c_str() );

					string rx = parseH.returnUntillStrip( " ", line );
					string ry = parseH.returnUntillStrip( " ", line );
					string rz = parseH.returnUntillStrip( " ", line );
					if ( !rx.empty() ) finish_rotX = atof( rx.c_str() );
					if ( !ry.empty() ) finish_rotY = atof( ry.c_str() );
					if ( !rz.empty() ) finish_rotZ = atof( rz.c_str() );
					
					finish.setIdentity();
					finish.setOrigin( btVector3( finish_posX,finish_posY,finish_posZ ) );
					finish.getBasis().setEulerZYX( finish_rotX, finish_rotY, finish_rotZ ); // -1.5707f = 90 degrees
				}

				else if ( param == "finishline_width" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) finish_width = atof( x.c_str() );
				}

				else if ( param == "finishline_height" )
				{
					string x = parseH.returnUntillStrip( " ", line );
					if ( !x.empty() ) finish_height = atof( x.c_str() );
				}
			}
		}
		// load OBJ file
		if ( maptype == "obj" )
		{
			string objfilename( befileMap.getDirectory() );
			objfilename.append(obj);

			m_model = BeModelSystem::Instance()->load( objfilename, btVector3(scaleX,scaleY,scaleZ) );

			if ( m_model != 0 )
			{
				btTransform rotoffset; rotoffset.setIdentity();
				rotoffset.getBasis().setEulerZYX( rotX, rotY, rotZ ); // -1.5707f = 90 degrees
				btTransform posoffset; posoffset.setIdentity();
				posoffset.setOrigin( btVector3(posX, posY, posZ) );

				btVector3 localInertia(0,0,0);

				//If there isnt physics_materials.xml create a single Trianglemesh with default physics material
				if( !loadPhysicsMaterials(befileMap.getDirectory() + "physics_materials.xml") || m_physicsMaterialMap.size() == 0 )
				{
					fprintf( stderr, "WARNING: No or invalid physics_materials.xml, using default physics material for entire map\n" ); 
					boost::shared_ptr<Bodypart> bodypart = body.addBodyPart_Rigid_Trianglemesh( m_model, btVector3(scaleX,scaleY,scaleZ), 0, localInertia, posoffset*rotoffset, BeColor( 1.0f, 1.0f, 1.0f, 1.0f ), 0, 0 );

					const BePhysicsMaterial mat;
					bodypart->setRestitution(mat.getRestitution());
					bodypart->setFriction(mat.getFriction());
				}
				else
				{
					//Otherwise create a Trianglemesh for each material
					btScalar* const vertices = m_model->vertices.get();
					unsigned int numVertices = m_model->getNumberVertices();
					int* const indices = m_model->indices.get(); 
					const std::vector<Facematerial> &facematerials = m_model->facematerials;

					for ( unsigned int i=0; i < facematerials.size(); i++ )
					{
						const Facematerial& facematerial = facematerials[i];
						PhysicsMaterialMap::iterator it = m_physicsMaterialMap.find(facematerial.material);
						if( it != m_physicsMaterialMap.end() )
						{
							const BePhysicsMaterial mat = it->second;
							boost::shared_ptr<Bodypart> bodypart = body.addBodyPart_Rigid_Trianglemesh((btVector3*)(vertices),numVertices,&indices[facematerial.faceindex_first], facematerial.faceindex_number, 0, localInertia, posoffset*rotoffset, BeColor( 1.0f, 1.0f, 1.0f, 1.0f ), 0, 0 );
							bodypart->setRestitution(mat.getRestitution());
							bodypart->setFriction(mat.getFriction());
						}
					}

				}

				btTransform player_pos_offset; player_pos_offset.setIdentity();
				player_pos_offset.setOrigin( btVector3( player_posX, player_posY, player_posZ ) );

				btTransform player_rot_offset; player_rot_offset.setIdentity();
				player_rot_offset.getBasis().setEulerZYX( player_rotX, player_rotY, player_rotZ ); // -1.5707f = 90 degrees

				startgrid = player_pos_offset * player_rot_offset;
			}
		}

	}

}

bool MeshMap::loadPhysicsMaterials(const std::string& path)
{
	//TODO: use BeFileSystem to find the correct path
	TiXmlDocument document;
	const bool result = document.LoadFile( path.c_str() );
	if(result)
	{
		const TiXmlElement* const rootElementPtr=document.RootElement();
		if(rootElementPtr)
		{
			const TiXmlElement& rootElement=(*rootElementPtr);
			const TiXmlElement* elementPtr=rootElement.FirstChildElement();
			while(elementPtr)
			{
				const TiXmlElement& element=(*elementPtr);
				if(element.ValueTStr()=="physics_material")
				{
					std::string texturePath;
					float friction = 1;
					float restitution = 0;
					BeXmlHelper<3> xmlHelper;
					xmlHelper.addString("texture_path", texturePath);
					xmlHelper.addFloat("friction", friction);
					xmlHelper.addFloat("restitution", restitution);
					xmlHelper.parse(element);

					m_physicsMaterialMap[texturePath]=BePhysicsMaterial(friction,restitution);
				}
				elementPtr=element.NextSiblingElement();
			}
		}
		return true;
	}
	return false;
}


MeshMap::~MeshMap()
{
}

void MeshMap::draw()
{
	if ( m_model )
	{
		m_model->draw( body.m_bodyparts[0]->drawposition );
	}
}



